package com.wangqi;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Mojo(name = "buildinfo", defaultPhase = LifecyclePhase.PACKAGE)
public class ResourcesMojo extends AbstractMojo {

    @Parameter(readonly = true, defaultValue = "${project}")
    private MavenProject project;

    @Parameter(property = "env")
    private String env;

    @Parameter(readonly = true, defaultValue = "${project.packaging}")
    private String packaging;

    @Parameter(readonly = true, defaultValue = "${project.build.directory}")
    private File directory;

    @Parameter(readonly = true, defaultValue = "${project.artifactId}-${project.version}")
    private String packageName ;

    @Parameter(readonly = true, defaultValue = "${project.build.finalName}")
    private String finalName ;

    @Parameter(readonly = true, property = "classesDir", defaultValue = "WEB-INF/classes")
    private String classesDir;

    public void execute() {
        if (!isSupported(packaging)) {
            getLog().info(String.format("Ignoring packaging %s", packaging));
            return;
        }
        if (env == null || env.equals("")) {
            getLog().info("No env is specified, so do local package");
            return;
        }

        File confDir = new File(project.getBasedir().getPath(), "conf/"+env);
        if (confDir.exists() && confDir.isDirectory()) {
            try {
                if(StringUtils.isNotEmpty(finalName)){
                    FileUtils.copyDirectory(confDir, new File(directory, finalName+"/"+classesDir));
                }else{
                    FileUtils.copyDirectory(confDir, new File(directory, packageName+"/"+classesDir));
                }
            } catch (IOException e) {
                getLog().error("Copy resource failed", e);
            }

            Map<String, ConfigFile> configMap = new HashMap<String, ConfigFile>();
            try {
                readConfigFile(classesDir, confDir, configMap);
                File finalPackage;
                if(StringUtils.isNotEmpty(finalName)){
                    finalPackage = new File(directory, finalName+"."+packaging);
                }else{
                    finalPackage = new File(directory, packageName+"."+packaging);
                }
                if(finalPackage == null){
                    getLog().error("Create " + packaging + " failed");
                }
                new JarProccessor().process(configMap, finalPackage, getLog());
                int changedFileCount = getFileCount(confDir);
                getLog().info("The count of replaced configuration file is : " + changedFileCount);
            } catch (Exception e) {
                getLog().error("Process " + packaging + " failed: " + e.getMessage(), e);
            }

        } else {
            getLog().error("The specified config directory is invalid: " + confDir.getAbsolutePath());
        }

    }

    private boolean isSupported(String packaging) {
        return "war".equals(packaging) || "jar".equals(packaging);
    }
    private int getFileCount(File dir) {
        if (dir == null || dir.isFile() || !dir.exists()) {
            return 0;
        }

        int count = 0;
        for (File file : dir.listFiles()) {
            if (file.isFile()) {
                count++;
            } else {
                count = count + getFileCount(file);
            }
        }

        return count;
    }

    /**
     * Build a map with key:path of file, value: Detail File information.
     *
     * @param dir directory need to be spanned
     * @return
     */
    private Map<String, ConfigFile> readConfigFile(String classes, File dir, Map<String, ConfigFile> map) throws IOException {
        if (dir == null || dir.isFile() || !dir.exists()) {
            return map;
        }

        for (File file : dir.listFiles())
            if (file.isDirectory()) {
                readConfigFile(classes, file, map);
                continue;
            } else {
                String path = classes + "/" + FileUtils.filename(file.getName());
                byte[] content = readByteContent(file);
                ConfigFile cf = new ConfigFile();
                cf.setPath(path);
                cf.setContent(content);
                map.put(path, cf);
            }

        return map;
    }

    private static byte[] readByteContent(File file) throws IOException {
        if (file == null || file.isDirectory()) {
            return null;
        }

        FileInputStream fis = new FileInputStream(file);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            byte[] b = new byte[1024];
            int count = 0;
            while ((count = fis.read(b)) != -1) {
                outputStream.write(b, 0, count);
            }
        } finally {
            if (fis != null) {
                fis.close();
            }
        }

        return outputStream.toByteArray();
    }

}
